#include "P2Defs.h"


#include "copyright.h"
#include "ProcessManager.h"
#include "system.h"
#include "machine.h"
#include "Process.h"
#include "addrspace.h"
#include "synch.h"

ProcessManager::ProcessManager() : processTable(MAX_PROCESSES) {
    noOfProcess = 0;
    pmLock = new Lock("ProcessTable");
} /* constructor */

SpaceId ProcessManager::Exec(char *progName) {
    //disable interrupts
    //IntStatus oldStatus = interrupt->SetLevel(IntOff);

    DEBUG('D', "ProcessManager:Exec start for %s.\n", progName);

    pmLock -> Acquire();

    if (noOfProcess >= MAX_PROCESSES) {
        DEBUG('1', "ProcessManager:  ### Error: No room for Exec(%s). Max processes are running... \n", progName);
        //restore interupts and return
        //interrupt->SetLevel(oldStatus);
        pmLock -> Release();
        return -1;
    }

    DEBUG('1', "ProcessManager:  In ProcessManager :: Exec(%s). Currently Running %d processes... \n", progName, noOfProcess);

    int ret;

    // create an addres-space
    AddrSpace *space = new AddrSpace();
    DEBUG('1', "ProcessManager:  Address space created for : Exec(%s) \n", progName);

    //crate a process, PID is the entry in the ProcessTable .
    Process* p = new Process(space, progName);
    int pid = processTable.Put((void*) p);
    p -> setPID(pid);
    space -> pid = pid;
    
    DEBUG('1', "ProcessManager:  New Process crated. PID:%d  Exec(%s) \n", p->getPID(), progName);

    // call process routine to load file and all...
    if (p -> StartNewProcess() != -1) {
        //process was created succefully, increment the no of processes
        noOfProcess++;
        DEBUG('1', "ProcessManager:  New Process create succefully for \"%s\" ...\n", progName);
        // if every thign is fine than add this thread to the new process.Restore interuppts and return
        //interrupt->SetLevel(oldStatus);
        pmLock -> Release();

        DEBUG('D', "ProcessManager:Exec end for %s.\n", progName);

        return pid;
    }

    // if you are here than it failed to create new process. :(
    DEBUG('1', "ProcessManager:  ### Error: Failed to create a new process for %s ...\n", progName);
    //interrupt->SetLevel(oldStatus);
    pmLock -> Release();

    DEBUG('D', "ProcessManager:Exec end for %s.\n", progName);

    return -1;

} /* Exec */

int ProcessManager::createNewThread(unsigned int functionPtr) {
    //disable interrunpts
    //IntStatus oldStatus = interrupt->SetLevel(IntOff);

    DEBUG('D', "ProcessManager:CreateNewThread start.\n");

    pmLock-> Acquire();
    DEBUG('1', "ProcessManager: Inside createNewThread..\n");
    Process* whichProcess = currentThread -> getParentProcess();

    if (whichProcess == NULL || !(processTable.Get(whichProcess->getPID()))) {
        DEBUG('1', "ProcessManager: Error: Thread %s doesn't belong to any process. Returning...\n", currentThread -> getName());
        pmLock -> Release();
        //interrupt->SetLevel(oldStatus);

        DEBUG('D', "ProcessManager:CreateNewThread start.\n");
        return -1;
    }

    //parent is there, add a new thread in it
    int forkRes = whichProcess -> addNewThread(functionPtr);

    if (forkRes == 1) {
        DEBUG('1', "ProcessManager: Thread forked successfully...\n");
    } else {
        DEBUG('1', "ProcessManager: Error in forking a new thread...\n");
    }

    //restore interrupts
    //interrupt->SetLevel(oldStatus);
    pmLock -> Release();

    DEBUG('D', "ProcessManager:CreateNewThread end.\n");

    return 1;

}

void ProcessManager::Exit(int exitCode) {

    //disable interrupts
    //IntStatus oldStatus = interrupt->SetLevel(IntOff);

    DEBUG('D', "ProcessManager:Exit start. Exit code=%d\n", exitCode);
    
    pmLock -> Acquire();
    
    if (noOfProcess == 0) {
        DEBUG('1', "ProcessManager:  ### Error: No process running. ...\n");
        DEBUG('D', "ProcessManager:Exit end. Exit code=%d\n", exitCode);
         pmLock -> Release() ;
        return;
    }

    // get current thead's process.
    Process* p = currentThread -> getParentProcess();
    if (p == NULL || !(processTable.Get(p->getPID()))) {
        DEBUG('1', "ProcessManager:  Error: Thread %s does not belong to any running process. Returning...\n", currentThread -> getName());
        //interrupt->SetLevel(oldStatus);

        DEBUG('D', "ProcessManager:Exit end. Exit code=%d. with Error\n", exitCode);
        
        pmLock -> Release();
        return;
    }

    //forward this exit call to currentThread's process
    int pExitStatus = p -> exitProcess(exitCode);

    if (pExitStatus == -1) {
        DEBUG('1', "ProcessManager:  ### Error: Error in terminating process  %s (pid: %d).\n", p -> getPName(), p->getPID());
    } else if (pExitStatus == 0) {
        // it was the last thread in process. Delete the process and free it's addres-space.
        // MemoryManager *m = MemoryManager::GetMemoryManagerInstance();
        // m -> freeAddrSpace(p -> getProcessAddressSpace());

        if (noOfProcess == 1) {
            //has no more processes to execute. Terminate nachos.
            noOfProcess--;
            DEBUG('1', "ProcessManager: Last process \"%s\" (pid: %d) has finished execution. Exit status: %d. Now Halting...\n", p -> getPName(), p->getPID(), exitCode);
            printf("ProcessManager:  Last process \"%s\" (pid: %d) has finished execution. Exit status: %d. Now Halting...\n", p -> getPName(), p->getPID(), exitCode);
            //restore the interrupts
            //interrupt->SetLevel(oldStatus);
            pmLock -> Release();
            // this is the only process. Terminate the nachos.
            interrupt -> Halt();
        } else {
            noOfProcess--;
            //some more processes are there in the process table
            //remove the currently being executed process
            DEBUG('1', "ProcessManager:  Process \"%s\" (pid: %d) has finished execution. Exit status: %d...\n", p -> getPName(), p->getPID(), exitCode);
            printf("ProcessManager:  Process \"%s\" (pid: %d) has finished execution. Exit status: %d...\n", p -> getPName(), p->getPID(), exitCode);

            p->getProcessAddressSpace() -> onProcessEnded() ;
            
            processTable.Remove(p -> getPID());
            //restore the interrupts
            //interrupt->SetLevel(oldStatus);
            pmLock -> Release();
            currentThread -> Finish();
        }

    } else {
        DEBUG('1', "ProcessManager:  Process \"%s\" (pid: %d) has some threads running.Calling Finish() on current thread...\n", p -> getPName(), p->getPID());
        //restore the interrupts
        //interrupt->SetLevel(oldStatus);
        pmLock -> Release();
        //call the current thread's finish method.
        currentThread -> Finish();
    }

    pmLock -> Release() ;
    DEBUG('D', "ProcessManager:Exit end. Exit code=%d\n", exitCode);

    return ;
}

// returns the process pointer for the passed process ID

Process* ProcessManager::getProcess(int pid) {

    pmLock -> Acquire() ;

    Process *p = (Process*) processTable.Get(pid);
    
    pmLock-> Release() ;
    return p;
}

// handlers for lock reletead sys calls

int ProcessManager::createLock() {
    int ret;
    //disable interrupts
    //IntStatus oldStatus = interrupt->SetLevel(IntOff);
    pmLock -> Acquire();
    // get current thead's process.
    Process* p = currentThread -> getParentProcess();

    if (p == NULL || !(processTable.Get(p->getPID()))) {
        DEBUG('1', "ProcessManager:  Error: Thread %s does not belong to any running process. Returning...\n", currentThread -> getName());

        ret = -1;
    } else {
        ret = p -> CreateLock();
    }

    pmLock -> Release();
    //interrupt->SetLevel(oldStatus);
    return ret;
}

int ProcessManager::destroyLock(int loc) {
    int ret;
    //disable interrupts
    //IntStatus oldStatus = interrupt->SetLevel(IntOff);
    pmLock -> Acquire();

    // get current thead's process.
    Process* p = currentThread -> getParentProcess();
    if (p == NULL || !(processTable.Get(p->getPID()))) {
        DEBUG('1', "ProcessManager:  Error: Thread %s does not belong to any running process. Returning...\n", currentThread -> getName());
        ret = -1;
    } else {
        ret = p -> DestoryLock(loc);
    }
    //restore interrupts
    //interrupt->SetLevel(oldStatus);
    pmLock -> Release();
    return ret;
}

int ProcessManager::acquireLock(int loc) {
    int ret;
    //disable interrupts
    //IntStatus oldStatus = interrupt->SetLevel(IntOff);

    pmLock -> Acquire();
    // get current thead's process.
    Process* p = currentThread -> getParentProcess();
    if (p == NULL || !(processTable.Get(p->getPID()))) {
        DEBUG('1', "ProcessManager:  Error: Thread %s does not belong to any running process. Returning...\n", currentThread -> getName());
        ret = -1;
    } else {
        ret = p -> LockAcquire(loc);
    }
    //restore interrupts
    //interrupt->SetLevel(oldStatus);
    pmLock -> Release();
    return ret;
}

int ProcessManager::releaseLock(int loc) {
    int ret;
    //disable interrupts
    //IntStatus oldStatus = interrupt->SetLevel(IntOff);
    pmLock -> Acquire();

    // get current thead's process.
    Process* p = currentThread -> getParentProcess();
    if (p == NULL || !(processTable.Get(p->getPID()))) {
        DEBUG('1', "ProcessManager:  Error: Thread %s does not belong to any running process. Returning...\n", currentThread -> getName());
        ret = -1;
    } else {
        ret = p -> LockRelease(loc);
    }
    //restore interrupts
    //interrupt->SetLevel(oldStatus);
    pmLock -> Release();

    return ret;
}

//handelers for Condition related sys calls

int ProcessManager::createCondition() {
    int ret;
    //disable interrupts
    //IntStatus oldStatus = interrupt->SetLevel(IntOff);
    pmLock -> Acquire();

    // get current thead's process.
    Process* p = currentThread -> getParentProcess();
    if (p == NULL || !(processTable.Get(p->getPID()))) {
        DEBUG('1', "ProcessManager:  Error: Thread %s does not belong to any running process. Returning...\n", currentThread -> getName());
        ret = -1;
    } else {
        ret = p -> CreateCondition();
    }
    //restore interrupts
    //interrupt->SetLevel(oldStatus);
    pmLock -> Release();

    return ret;
}

int ProcessManager::destroyCondition(int cond) {
    int ret;
    //disable interrupts
    //IntStatus oldStatus = interrupt->SetLevel(IntOff);
    pmLock -> Acquire();

    // get current thead's process.
    Process* p = currentThread -> getParentProcess();
    if (p == NULL || !(processTable.Get(p->getPID()))) {
        DEBUG('1', "ProcessManager:  Error: Thread %s does not belong to any running process. Returning...\n", currentThread -> getName());
        ret = -1;
    } else {
        ret = p -> DestroyCondition(cond);
    }
    //restore interrupts
    //interrupt->SetLevel(oldStatus);
    pmLock -> Release();

    return ret;
}

int ProcessManager::signalOnCondition(int cond, int loc) {
    int ret;
    //disable interrupts
    //IntStatus oldStatus = interrupt->SetLevel(IntOff);
    pmLock -> Acquire();

    // get current thead's process.
    Process* p = currentThread -> getParentProcess();
    if (p == NULL || !(processTable.Get(p->getPID()))) {
        DEBUG('1', "ProcessManager:  Error: Thread %s does not belong to any running process. Returning...\n", currentThread -> getName());
        ret = -1;
    } else {
        ret = p -> SignalCondition(cond, loc);
    }
    //restore interrupts
    //interrupt->SetLevel(oldStatus);

    pmLock -> Release();

    return ret;
}

int ProcessManager::waitOnCondition(int cond, int loc) {
    int ret;
    //disable interrupts
    //IntStatus oldStatus = interrupt->SetLevel(IntOff);
    pmLock -> Acquire();

    // get current thead's process.
    Process* p = currentThread -> getParentProcess();
    if (p == NULL || !(processTable.Get(p->getPID()))) {
        DEBUG('1', "ProcessManager:  Error: Thread %s does not belong to any running process. Returning...\n", currentThread -> getName());
        ret = -1;
    } else {
        ret = p -> WaitCondition(cond, loc);
    }
    //restore interrupts
    //interrupt->SetLevel(oldStatus);
    pmLock -> Release();

    return ret;
}

int ProcessManager::broadCastOnCondition(int cond, int loc) {
    int ret;
    //disable interrupts
    //IntStatus oldStatus = interrupt->SetLevel(IntOff);

    pmLock -> Acquire();
    // get current thead's process.
    Process* p = currentThread -> getParentProcess();
    if (p == NULL || !(processTable.Get(p->getPID()))) {
        DEBUG('1', "ProcessManager:  Error: Thread %s does not belong to any running process. Returning...\n", currentThread -> getName());
        ret = -1;
    } else {
        ret = p -> BroadcastCondition(cond, loc);
    }
    //restore interrupts
    //interrupt->SetLevel(oldStatus);
    pmLock -> Release();

    return ret;
}
